package de.mssp.ssp;

import de.tum.in.bluetooth.BluetoothClientP2P;
import de.tum.in.bluetooth.gui.BluetoothService;
import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.drawable.Drawable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Animation.AnimationListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;


public class Ssp extends Activity implements SensorEventListener, AnimationListener, OnClickListener {

	// Intent Codes
	private static final int FIND_ENEMY = 1;
	
	//Possible values
	public enum Selected 
	{
		STEIN, SCHERE, PAPIER, NOTHING
	}
	
	//Result values
	public enum Result
	{
		WIN, DRAW, LOSS
	}
	
	//Logical steps in the gameplay
	public enum Steps
	{
		WAITING_FOR_ENEMY,
		CONNECTING_TO_ENEMY,
		WAITING_FOR_CHOICE,
		MADE_MY_CHOICE,
		SHAKE_IT,
		CHANGED_RESULTS
	}
	
	//Defaults
	private Steps currentStep = Steps.WAITING_FOR_ENEMY;
	
	private Selected myResult = Selected.NOTHING;
	private String enemyResult = null;
	
	private boolean enemyMadeChoice = false;
	
	private ImageView activeImage;
	
	//Images & Animation
	private int zaehler = 0;
	private Drawable stein;
	private Drawable schere;
	private Drawable papier;
	private Animation animation;
	
	private boolean animationStarted=false;
	private boolean shakingStarted=false;
	private boolean animationEnded=false;
	
	private SensorManager sensorManager;
	
	//GUI
	private View view;
	private TextView status;
	private LinearLayout buttonContainer;
	
	private Button schereButton;
	private Button steinButton;
	private Button papierButton;
	
	private Button connectButton;
	private Button restartButton;
	private Button disconnectButton;

	//Bluetooth Handling
	private Handler connectionHandler;
	private BluetoothClientP2P enemy;
	private boolean server = false;
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    	
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        
		activeImage = (ImageView) findViewById(R.id.activeImage);
		
		sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
		
		//Images
		stein = getResources().getDrawable(R.drawable.contour_massage_stone);
		schere = getResources().getDrawable(R.drawable.schere);
		papier = getResources().getDrawable(R.drawable.papier);
		
		zaehler = 0;
		
		status = (TextView)findViewById(R.id.status);
		buttonContainer = (LinearLayout)findViewById(R.id.menulayout);
		
		//Create all buttons
		connectButton = new Button(this);
		connectButton.setText("Connect to Enemy");
		connectButton.setOnClickListener(this);
		
		restartButton = new Button(this);
		restartButton.setText("Restart Game");
		restartButton.setOnClickListener(this);
		
		disconnectButton = new Button(this);
		disconnectButton.setText("Disconnect from Enemy");
		disconnectButton.setOnClickListener(this);
		
		steinButton = new Button(this);
		steinButton.setText("Stein");
		steinButton.setOnClickListener(this);
		
		schereButton = new Button(this);
		schereButton.setText("Schere");
		schereButton.setOnClickListener(this);
		
		papierButton = new Button(this);
		papierButton.setText("Papier");
		papierButton.setOnClickListener(this);
		
		//build menu
		updateMenu();
		
		//Bluetooth Handling
		connectionHandler = new Handler()
		{
			@Override
			public void handleMessage(Message m) 
			{
				switch (m.what) 
				{
					case BluetoothClientP2P.EXCEPTION:
						//Bluetooth exception, reset game
						currentStep=Steps.WAITING_FOR_ENEMY;
						updateMenu();
						Toast.makeText(getApplicationContext(), m.obj.toString(), Toast.LENGTH_LONG).show();
						break;
					case BluetoothClientP2P.DISCONNECTED:
						//Currently connecting to an enemy
						currentStep=Steps.WAITING_FOR_ENEMY;
						updateMenu();
						Toast.makeText(getApplicationContext(), m.obj.toString(), Toast.LENGTH_LONG).show();
						break;
					case BluetoothClientP2P.CONNECTING:
						//Currently connecting to an enemy
						status.setText("Status: Connecting...");
						break;
					case BluetoothClientP2P.REQUESTED:
						//You are the not the chef, somebody wants to beat you!
						server = false;
						currentStep=Steps.WAITING_FOR_CHOICE;
						updateMenu();
						break;
					case BluetoothClientP2P.CONNECTED:
						//You are the chef, your enemy connected well
						server = true;
						currentStep=Steps.WAITING_FOR_CHOICE;
						updateMenu();
						break;
					case BluetoothClientP2P.RECEIVED_DATA:
						//Bluetooth received data
						handleData(m.obj.toString());
						break;
					default:
						Toast.makeText(getApplicationContext(), "Unknown handler message", Toast.LENGTH_LONG).show();
						break;
				}
			}
		};
		
		//Start Bluetooth idle
		enemy = new BluetoothClientP2P(connectionHandler, "SSP_Bluetooth_Service");
		enemy.start();
    }
    
    public void postResults(Result r)
    {
    	//Compare Results
		switch(r)
		{
		case WIN:
			enemy.sendString("YOU LOOSE");
			Toast.makeText(getApplicationContext(), "YOU WON", Toast.LENGTH_LONG).show();
			break;
		case LOSS:
			enemy.sendString("YOU WIN");
			Toast.makeText(getApplicationContext(), "YOU LOOSE", Toast.LENGTH_LONG).show();
			break;
		case DRAW:
			enemy.sendString("DRAW");
			Toast.makeText(getApplicationContext(), "DRAW", Toast.LENGTH_LONG).show();
			break;
		}
    }
    
    public void handleData(String data)
    {
    	if (currentStep == Steps.SHAKE_IT)
    	{
    		if(server)
			{
    			enemyResult = data;
				//Compare Results
    			if(animationEnded)
    				postResults(getResult(data));
				
			}else{
				// I am not the server, so he should told me who won
				String winner = data;
				Toast.makeText(getApplicationContext(), winner, Toast.LENGTH_LONG).show();
			}
    		
    		if(animationEnded)
    		{
    			currentStep=Steps.CHANGED_RESULTS;
    			updateMenu();
    		}
    	}
    	if (currentStep == Steps.MADE_MY_CHOICE)
		{
    		//if i made my choice, we can start shaking
			currentStep=Steps.SHAKE_IT;
			updateMenu();
		}
    	if (currentStep == Steps.WAITING_FOR_CHOICE || currentStep == Steps.CHANGED_RESULTS) 
		{
			if(data.equals("READY"))
			{
				//Ready Tag -> enemy made his choice
				enemyMadeChoice=true;
			}
		}
    }
    
    /**
     * This Method evaluates the winner
     * @param enemyResult - Result of your enemy
     * @return WIN, if you won
     */
    public Result getResult(String enemyResult)
    {
    	String result = myResult.toString();
    	
    	if(beats(result,enemyResult))
    		return Result.WIN;
    	
    	if(beats(enemyResult,result))
    		return Result.LOSS;
    	
    	return Result.DRAW;	
    }
    
    /**
     * Compares two choices
     * @param a _ Choice A
     * @param b _ Choice B
     * @return true if A is better than b
     */
    public boolean beats(String a, String b)
    {
    	if(a.equals(Selected.STEIN.toString()) && b.equals(Selected.SCHERE.toString()))
    		return true;
    	
    	if(a.equals(Selected.PAPIER.toString()) && b.equals(Selected.STEIN.toString()))
    		return true;
    	
    	if(a.equals(Selected.SCHERE.toString()) && b.equals(Selected.PAPIER.toString()))
    		return true;
    	
    	return false;
    		
    }
    /**
     * Updates the menu due to the currentStep
     */
    private void updateMenu()
    {
    	buttonContainer.removeAllViews();
    	
    	switch (currentStep) 
    	{
		case WAITING_FOR_ENEMY:
		{
			status.setText("Status: Connect to your enemy to play");
			buttonContainer.addView(connectButton);
			restart();
		}
			break;
		case CONNECTING_TO_ENEMY:
		{
			status.setText("Status: Connecting to enemy");
		}
		break;
		case WAITING_FOR_CHOICE:
		{
			status.setText("Status: Make your choice");
			buttonContainer.addView(steinButton);
			buttonContainer.addView(schereButton);
			buttonContainer.addView(papierButton);
		}
		break;
		case MADE_MY_CHOICE:
		{
			status.setText("Status: Wait for your enemies choice");
		}
		break;
		case SHAKE_IT:
		{
			status.setText("Status: Shake your device");
		}
		break;
		case CHANGED_RESULTS:
		{
			status.setText("Status: Play a new round or find new enemy");
			buttonContainer.addView(restartButton);
			buttonContainer.addView(disconnectButton);
			restart();
		}
		break;
		default:
			status.setText("Status: Unknown");
			break;
		}
    }
    
	public void onClick(View v) 
	{
		if(v == connectButton)
		{
			//Open Devicelist
			Intent intent = new Intent(this, BluetoothService.class);
			startActivityForResult(intent, FIND_ENEMY);
		}
		
		if(v == disconnectButton)
		{
			//disconnect from enemy
			enemy.disconnect();
		}
		
		if(v == restartButton)
		{
			//new round ssp
			currentStep = Steps.WAITING_FOR_CHOICE;
			updateMenu();
			//restart();
		}
		
		//Choices
		if(v == steinButton)
			myResult = Selected.STEIN;
		if(v == schereButton)
			myResult = Selected.SCHERE;
		if(v == papierButton)
			myResult = Selected.PAPIER;
		
		if(v == steinButton || v == schereButton || v == papierButton)
		{
			//Send to enemy that you made your choice
				enemy.sendString("READY");
				currentStep=Steps.MADE_MY_CHOICE;
				updateMenu();
				
				if(enemyMadeChoice)
				{
					//if enemy already made his choice
					//you can start shaking
					currentStep=Steps.SHAKE_IT;
					updateMenu();
				}
		}
	}
    
    @Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) 
	{
		super.onActivityResult(requestCode, resultCode, data);
		
		if (requestCode == FIND_ENEMY) 
		{
			//Connect to other device
			if (resultCode == RESULT_OK) 
			{
				//Connect to selected enemy
				BluetoothDevice partner = (BluetoothDevice) data.getExtras().get("device");
				enemy.connectToDevice(partner);
			}
		}
	}
    
    @Override
    public void onStart()
    {
    	super.onStart();

    	//Prepare animation
    	activeImage.setImageDrawable(stein);
		
    	//animation = AnimationUtils.loadAnimation(this,android.R.anim.fade_in);
    	animation = AnimationUtils.loadAnimation(this,R.anim.fake);
    	animation.setDuration(500);
    	animation.setAnimationListener(this);
    	animation.setRepeatCount(13);
	  	  
    	view = findViewById(R.id.slots);
    }

    /** Register for the updates when Activity is in foreground */
	@Override
	protected void onResume() 
	{
		super.onResume();
		sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_UI);
	}

	/** Stop the updates when Activity is paused */
	@Override
	protected void onPause() 
	{
		super.onPause();
		//Stop listening
		sensorManager.unregisterListener(this);
	}
	
	@Override
	protected void onDestroy()
	{
		super.onDestroy();
		
		Log.i("SSP","DESTROY!!");
		if(enemy != null)
			enemy.cancel();
	}
	
	
	/** Restarts the Animation*/
	public void restart()
	{
		activeImage.setImageDrawable(stein);
		animationStarted = false;
		animationEnded = false;
		shakingStarted = false;
		zaehler = 0;
		
		enemyMadeChoice=false;
		myResult = Selected.NOTHING;
		enemyResult = null;
	}

    /** Is called when the sensor changes. */
	public void onSensorChanged(SensorEvent event) 
	{
		//If i am not in shakemode, ignore shakes
		if(currentStep != Steps.SHAKE_IT)
			return;
		
		float acceleration = Math.abs(event.values[0])+Math.abs(event.values[1])+Math.abs(event.values[2]);
		//if the user shakes the device
		
		if(acceleration > 18){
			shakingStarted = true;
		}
		
		else if(acceleration < 12 && shakingStarted && !animationStarted)
		{
			view.startAnimation(animation);
			animationStarted = true;
		}
	}


	//Animation ended, time to share the results
	public void onAnimationEnd(Animation animation) 
	{
		animationEnded=true;
		
		switch(myResult)
		{
		case STEIN:
			activeImage.setImageDrawable(stein);
			break;
		case SCHERE:
			activeImage.setImageDrawable(schere);
			break;
		case PAPIER:
			activeImage.setImageDrawable(papier);
			break;
		}
		
		//send result to server
		if(enemy.isConnected)
			if(!server)
				enemy.sendString(myResult.toString());
			else
				if(enemyResult != null){
					postResults(getResult(enemyResult.toString()));
					
					currentStep=Steps.CHANGED_RESULTS;
	    			updateMenu();
				}
				
	}

	/**
	 * Organize animation
	 */
	public void onAnimationRepeat(Animation animation) 
	{
		int current = (zaehler + 1) % 3;
		if (current == 0) activeImage.setImageDrawable(schere);
		if (current == 1) activeImage.setImageDrawable(stein);
		if (current == 2) activeImage.setImageDrawable(papier);
		animation.setDuration(zaehler*zaehler*10);
		zaehler++;
	}


	public void onAnimationStart(Animation animation) {
		//Nothing to do
	}
	
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
		//Nothing to do
	}

}